import threading
import copy

import tornado

from coin.base.config import Config
from coin.flow.simple_queue import SimpleQueue
from coin.exchange.base.order_gateway import OrderSide
from coin.exchange.bitfinex_v2.kr_rest.product import BitfinexProduct
from coin.exchange.base.kr_rest.order_base import OrderBase
from coin.exchange.bitfinex_v2.order_gateway import BitfinexV2OrderGateway


class UnknownOrderSide(Exception):
  pass


class OrderGatewayWrapper(object):
  def __init__(self, products, key_file):
    self.ioloop = None
    self.products = products
    config = Config.from_file(key_file)
    self.og = BitfinexV2OrderGateway(products, config)
    event = threading.Event()
    self.thread = threading.Thread(target=self._start, args=(event,))
    self.thread.start()
    event.wait()

  def stop(self):
    self.ioloop.stop()

  def _start(self, event):
    self.ioloop = tornado.ioloop.IOLoop()
    self.queue = SimpleQueue('bitfinex_og_queue')
    self.og.start(self.queue, self.ioloop)
    event.set()
    self.ioloop.start()

  def _query_balance(self, event):
    self.balance = copy.deepcopy(self.og.get_balance())
    event.set()

  def _query_working_orders(self, event):
    self.working_orders = copy.deepcopy(list(self.og.get_working_order()))
    event.set()

  def _query_submit(self, product, side, price, qty):
    ccy_pair = BitfinexProduct.FromStr(product.symbol)
    if side == "buy" or side == "bid":
      order_side = OrderSide.BUY
    elif side == "sell" or side == "ask":
      order_side = OrderSide.SELL
    else:
      raise UnknownOrderSide(side)
    self.og.submit(ccy_pair, "%.5f" % price, "%.5f" % qty, order_side)

  def _query_cancel_product(self, product, ignore_error):
    ccy_pair = BitfinexProduct.FromStr(product.symbol)
    orders = self.og.order_ids_by_product(ccy_pair)
    if len(orders) > 0:
      self.og.cancel(orders, ignore_error=ignore_error)

  def query_balance(self):
    event = threading.Event()
    event.clear()
    self.ioloop.add_callback(self._query_balance, event)
    event.wait()  # making it blocking
    return self.balance

  def query_working_orders(self):
    event = threading.Event()
    event.clear()
    self.ioloop.add_callback(self._query_working_orders, event)
    event.wait()  # making it blocking
    return self.working_orders

  def query_submit(self, product, side, price, qty):
    self.ioloop.add_callback(self._query_submit, product, side, price, qty)

  def query_cancel_product(self, product, ignore_error=True):
    self.ioloop.add_callback(self._query_cancel_product, product, ignore_error)


class BitfinexV2Order(OrderBase):
  ProductType = BitfinexProduct

  def __init__(self, products, key_file):
    OrderBase.__init__(self, products, key_file)
    self.client = OrderGatewayWrapper(products, key_file)

  def stop(self):
    self.client.stop()

  def query_balance(self):
    return self.client.query_balance()

  def query_available_balance(self):
    return self.client.query_available_balance()

  def query_submit(self, product, side, price, qty):
    self.client.query_submit(product, side, price, qty)

  def query_cancel(self, product, order_id):
    raise NotImplementedError()

  def query_cancel_product(self, product):
    self.client.query_cancel_product(product)

  def query_working_orders(self):
    return self.client.query_working_orders()
